package lumis.portal.presentation.perspective.publisher;

import lumis.portal.presentation.PortalModeUtil;
import lumis.portal.presentation.core.HTMLFragmentComponent;
import lumis.portal.presentation.core.ILayoutFileComponentFactory;
import lumis.portal.presentation.core.LayoutFileTag;
import lumis.portal.presentation.perspective.IPortalModeSPI;
import lumis.portal.presentation.perspective.admin.PortalStudioHeaderPanel;
import lumis.portal.presentation.perspective.user.UserPerspectiveMarkupFilter;

import java.lang.reflect.Constructor;

import javax.servlet.http.HttpServletRequest;

import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.parser.XmlTag;
import org.apache.wicket.protocol.http.WebRequest;

/**
 * Filters the perspective for the Publisher mode.
 * @version $Revision: 13214 $ $Date: 2011-07-07 21:46:10 -0300 (Thu, 07 Jul 2011) $
 * @since 6.0.0
 */
public class PublisherPerspectiveMarkupFilter extends UserPerspectiveMarkupFilter
{
	/**
	 * The class constructor. 
	 * @param stripLayoutFileTagsAndAttributes is true if must removes the layout file tags and attributes else is false.
	 * @since 6.0.0
	 */
	public PublisherPerspectiveMarkupFilter(boolean stripLayoutFileTagsAndAttributes)
	{
		super(stripLayoutFileTagsAndAttributes);
	}

	@Override
	protected String getGenerateIdPrefix()
	{
		return "_layoutfile_";
	}

	@Override
	protected String getNamespaceURI()
	{
		return "http://www.lumis.com.br/lumisportal/xsd/layoutfile/page";
	}
	
	@Override
	protected String getDefaultNamespacePrefix()
	{
		return "page";
	}

	/**
	 * Factory used to create a {@link PublisherJavascriptHeadPanel}.
	 * 
	 * @version $Revision: 13214 $ $Date: 2011-07-07 21:46:10 -0300 (Thu, 07 Jul 2011) $
	 * @since 6.0.1
	 */
	private static class PublisherHeadJavascriptsComponentFactory implements ILayoutFileComponentFactory
	{
		private final boolean stripLayoutFileTagsAndAttributes;

		public PublisherHeadJavascriptsComponentFactory(boolean stripLayoutFileTagsAndAttributes)
		{
			this.stripLayoutFileTagsAndAttributes = stripLayoutFileTagsAndAttributes;
		}

		public Component createComponent(MarkupContainer container, MarkupStream markupStream, LayoutFileTag tag)
		{
			PublisherJavascriptHeadPanel pubilsherHeadPanel = new PublisherJavascriptHeadPanel(tag.getId());
			if (stripLayoutFileTagsAndAttributes)
				pubilsherHeadPanel.setRenderBodyOnly(true);
			return pubilsherHeadPanel;
		}
	}

	/**
	 * Factory used to create a {@link PublisherCssHeadPanel}.
	 * 
	 * @version $Revision: 13214 $ $Date: 2011-07-07 21:46:10 -0300 (Thu, 07 Jul 2011) $
	 * @since 6.0.1
	 */
	private static class PublisherCssHeadPanelComponentFactory implements
			ILayoutFileComponentFactory
	{
		private final boolean stripLayoutFileTagsAndAttributes;

		public PublisherCssHeadPanelComponentFactory(boolean stripLayoutFileTagsAndAttributes)
		{
			this.stripLayoutFileTagsAndAttributes = stripLayoutFileTagsAndAttributes;
		}

		public Component createComponent(MarkupContainer container, MarkupStream markupStream,
				LayoutFileTag tag)
		{
			PublisherCssHeadPanel pubilsherHeadPanel = new PublisherCssHeadPanel(tag.getId());
			if (stripLayoutFileTagsAndAttributes)
				pubilsherHeadPanel.setRenderBodyOnly(true);
			return pubilsherHeadPanel;
		}
	}
	
	/**
	 * Factory used to create a {@link HTMLFragmentComponent} that represents the publisher sub header loaded from 
	 * {@link IPortalModeSPI}.
	 *
	 * @version $Revision: 13214 $ $Date: 2011-07-07 21:46:10 -0300 (Thu, 07 Jul 2011) $
	 * @since 6.0.0
	 */
	private static class PublisherSubHeadComponentFactory implements ILayoutFileComponentFactory
	{
		public PublisherSubHeadComponentFactory(){}

		public Component createComponent(MarkupContainer container, MarkupStream markupStream, LayoutFileTag tag)
		{
			HttpServletRequest httpServletRequest = ((WebRequest) container.getRequest()).getHttpServletRequest();
			IPortalModeSPI portalMode = (IPortalModeSPI) PortalModeUtil.getPortalMode(httpServletRequest);
			HTMLFragmentComponent fragmentComponent;
			try 
			{
				Class<? extends HTMLFragmentComponent> componentClass = portalMode.getRenderComponentClass();
				Constructor<? extends HTMLFragmentComponent> declaredConstructor = componentClass.getDeclaredConstructor(String.class);
				fragmentComponent = declaredConstructor.newInstance(tag.getId());
			} 
			catch (Exception e) 
			{
				fragmentComponent = null;
				throw new RuntimeException(e);
			}
			
			return fragmentComponent;
		}
	}

	/**
	 * Factory used to create a {@link PortalStudioHeaderPanel}.
	 *
	 * @version $Revision: 13214 $ $Date: 2011-07-07 21:46:10 -0300 (Thu, 07 Jul 2011) $
	 * @since 6.0.0
	 */	
	private static class PublisherHeaderPanelFactory implements ILayoutFileComponentFactory
	{
		public Component createComponent(MarkupContainer container, MarkupStream markupStream, LayoutFileTag tag)
		{
			return new PortalStudioHeaderPanel(tag.getId());
		}
	}
	
	/**
	 * Indicates if the <code>headScripts</code> component has been included.
	 * @since 6.0.0
	 */
	private boolean headScriptsIncluded = false;
	
	/**
	 * Indicates whether the closing BODY element has been visited.
	 * @since 6.0.0
	 */
	private boolean bodyCloseVisited = false;

	/**
	 * Indicates whether the closing HEAD element has been visited.
	 * @since 6.0.0
	 */
	private boolean headCloseVisited = false;

	/**
	 * Indicates whether the CSS component has been included.
	 * @since 6.0.1
	 */
	private boolean cssIncluded = false;

	/**
	 * Tag put around the main page render body. Used to adjust for
	 * absolute positioning.
	 * @since 6.0.0
	 */
	private ComponentTag pageBodyOpenTag;

	@Override
	protected ComponentTag beforeProcessTag(ComponentTag componentTag)
	{
		componentTag = super.beforeProcessTag(componentTag);

		if (isHTMLElement(componentTag, "head"))
		{
			if (componentTag.isClose())
			{
				if (!headCloseVisited)
				{
					// push head
					componentTag.setModified(true);
					pushTag(componentTag);
					
					// include  publisher head just before head close
					componentTag = createComponentTag(componentTag, null,
							"publisherHeadJavascripts", XmlTag.OPEN_CLOSE);
					componentTag.setId(generateId("publisherHeadJavascripts"));
					componentTag = new LayoutFileTag(componentTag, new PublisherHeadJavascriptsComponentFactory(isStripLayoutFileTagsAndAttributes()));
					
					if (!headScriptsIncluded)
					{
						// headScripts must be included inside <head>
						pushTag(componentTag);
						componentTag = createComponentTag(componentTag, getNamespacePrefix(), "headScripts", XmlTag.OPEN_CLOSE);
						headScriptsIncluded = true;
					}
					
					headCloseVisited = true;
				}
			}
			else if (componentTag.isOpen())
			{
				if (!cssIncluded)
				{
					// push head
					pushTag(componentTag);

					// include publisher css
					componentTag = createComponentTag(componentTag, null, "publisherHeadCss",
							XmlTag.OPEN_CLOSE);
					componentTag.setId(generateId("publisherHeadCss"));
					componentTag = new LayoutFileTag(componentTag,
							new PublisherCssHeadPanelComponentFactory(
									isStripLayoutFileTagsAndAttributes()));

					cssIncluded = true;

				}
			}
		}

		if (isHTMLElement(componentTag, "body"))
		{
			if (componentTag.isOpen())
			{
				
				componentTag.setModified(true);

				// page body open
				pageBodyOpenTag = createComponentTag(componentTag, null, "div", XmlTag.OPEN);
				pageBodyOpenTag.put("style", "position:relative;");
				pageBodyOpenTag.put("id", "lumDivContainer");
				pushTag(pageBodyOpenTag);
				
				ComponentTag renderPublisherSubHeader = createComponentTag(componentTag, null, "div", XmlTag.OPEN_CLOSE);
				renderPublisherSubHeader.put("id", "LumisPublisherSubHeader");
				renderPublisherSubHeader.put("class", "cLumAdminHeader cLumAdminReset");
				renderPublisherSubHeader.setId(generateId("publisherSubHeader"));
				renderPublisherSubHeader = new LayoutFileTag(renderPublisherSubHeader, new PublisherSubHeadComponentFactory());					
				pushTag(renderPublisherSubHeader);

				// publisher header 
				ComponentTag renderPublisherHeader = createComponentTag(componentTag, null, "div", XmlTag.OPEN_CLOSE);
				renderPublisherHeader.put("id", "LumisPublisherHeader");
				renderPublisherHeader.put("class", "cLumAdminHeader cLumAdminReset");
				renderPublisherHeader.setId(generateId("publisherHeader"));
				renderPublisherHeader = new LayoutFileTag(renderPublisherHeader, new PublisherHeaderPanelFactory());					
				pushTag(renderPublisherHeader);
			}
			else if (componentTag.isClose())
			{
				if (!bodyCloseVisited)
				{
					componentTag.setModified(true);
					pushTag(componentTag);

					// close pageBodyOpenTag
					ComponentTag pageBodyCloseTag = createComponentTag(componentTag, null, "div", XmlTag.CLOSE);
					pageBodyCloseTag.setOpenTag(pageBodyOpenTag);
					componentTag = pageBodyCloseTag;
					
					bodyCloseVisited = true;
				}
			}
		}
		
		return componentTag;
	}
	
	@Override
	protected void afterProcessTag(ComponentTag componentTag)
	{
		super.afterProcessTag(componentTag);
		
		if (componentTag instanceof LayoutFileTag)
		{
			if ("headScripts".equals(componentTag.getName()))
				headScriptsIncluded = true;
		}
	}
	
	/**
	 * Returns true if the given tag corresponds to the given element name and 
	 * has no namespace.
	 * <p>
	 * The element name is compared case insensitively.
	 * 
	 * @param tag the given tag.
	 * @param elementName the given element name.
	 * @return true if the given tag corresponds to the given element name and has no name space.
	 * @since 6.0.0
	 */
	@Override
	protected boolean isHTMLElement(ComponentTag tag, String elementName)
	{
		return tag.getNamespace() == null && elementName.equalsIgnoreCase(tag.getName());
	}

}